﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Text;

using Storm.TextEditor.Editor.Text;
using Storm.TextEditor.Editor.Undo;
using Storm.TextEditor.Parser;
using Storm.TextEditor.Parser.Objects;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;

namespace Storm.TextEditor.Editor
{
	/// <summary>
	/// Represents a document in a TextEditor.
	/// </summary>
	[ToolboxItem(false)]
	public class SyntaxDocument
		: Component, IEnumerable
	{
		#region Fields

		private RowCollection parseQueue   = new RowCollection();
		private RowCollection keywordQueue = new RowCollection();
		private RowCollection visibleRows  = new RowCollection();

		private FormatRangeCollection formatRanges = null;
		private UndoBuffer            undoBuffer   = new UndoBuffer();
		private IParser               parser       = new DefaultParser();

		private object tag             = null;
		private bool   shouldResetRows = false;

		private bool parsed      = true;
		private bool folding     = true;
		private bool modified    = false;
		private bool captureMode = false;

		private long   version      = long.MinValue;
		private int    undoStep     = 0;
		private string languageFile = "";

		private Container           components   = null;
		private UndoBlockCollection captureBlock = null;
		private RowCollection       document     = new RowCollection();

		#region Events

		/// <summary>
		/// Occurs when the parsing of the SyntaxDocument has finished.
		/// </summary>
		public event EventHandler ParsingCompleted;

		/// <summary>
		/// Occurs when the undo buffer of the SyntaxDocument has changed.
		/// </summary>
		public event EventHandler UndoBufferChanged;

		/// <summary>
		/// Occurs when the SyntaxDocument is getting parsed.
		/// </summary>
		public event EventHandler Parsing;

		/// <summary>
		/// Occurs when the SyntaxDocument has changed.
		/// </summary>
		public event EventHandler Changed;

		/// <summary>
		/// Occurs when the SyntaxDocument has been modified.
		/// </summary>
		public event EventHandler ModifiedChanged;

		/// <summary>
		/// Occurs when a breakpoint has been added to the SyntaxDocument.
		/// </summary>
		public event RowEventHandler BreakpointAdded;

		/// <summary>
		/// Occurs when a breakpoint has been removed from the SyntaxDocument.
		/// </summary>
		public event RowEventHandler BreakpointRemoved;

		/// <summary>
		/// Occurs when a bookmark has been added to the SyntaxDocument.
		/// </summary>
		public event RowEventHandler BookmarkAdded;

		/// <summary>
		/// Occurs when a bookmark has been removed from the SyntaxDocument.
		/// </summary>
		public event RowEventHandler BookmarkRemoved;

		/// <summary>
		/// Occurs when a row in the SyntaxDocument has been parsed.
		/// </summary>
		public event ParserEventHandler RowParsed;

		/// <summary>
		/// Occurs when a row in the SyntaxDocument has been deleted.
		/// </summary>
		public event ParserEventHandler RowDeleted;

		#endregion

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the parsing queue of rows in the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public RowCollection ParseQueue
		{
			get { return parseQueue; }
			set { parseQueue = value; }
		}

		/// <summary>
		/// Gets or sets the keyword queue of rows in the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public RowCollection KeywordQueue
		{
			get { return keywordQueue; }
			set { keywordQueue = value; }
		}

		/// <summary>
		/// Gets or sets the visible rows in the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public RowCollection VisibleRows
		{
			get { return visibleRows; }
			set { visibleRows = value; }
		}

		/// <summary>
		/// Gets or sets the collection of FormatRanges in the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public FormatRangeCollection FormatRanges
		{
			get { return formatRanges; }
			set { formatRanges = value; }
		}

		/// <summary>
		/// Gets or sets the undo buffer of the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public UndoBuffer UndoBuffer
		{
			get { return undoBuffer; }
			set { undoBuffer = value; }
		}

		/// <summary>
		/// Gets or sets the parser of the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public IParser Parser
		{
			get { return parser; }
			set { parser = value; }
		}

		/// <summary>
		/// Gets or sets the tag of the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public object Tag
		{
			get { return tag; }
			set { tag = value; }
		}

		/// <summary>
		/// Gets or sets whether the SyntaxDocument should reset its rows.
		/// </summary>
		[Browsable(false)]
		public bool ShouldResetRows
		{
			get { return shouldResetRows; }
			set { shouldResetRows = value; }
		}
        
		/// <summary>
		/// Gets the number of rows in the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		public int Count
		{
			get { return document.Count; }
		}

		/// <summary>
		/// Gets or sets the text of the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string Text
		{
			get
			{
				int rowIndex = 0;
				StringBuilder stringBuilder = new StringBuilder();

				this.ParseAll(true);
				foreach (Row row in document)
				{
					if (rowIndex > 0)
						stringBuilder.Append(Environment.NewLine);

					row.MatchCase();
					stringBuilder.Append(row.Text);

					rowIndex++;
				}

				return stringBuilder.ToString();
			}
			set
			{
				this.ClearAll();
				this.Add("");

				this.InsertText(value, 0, 0);
				undoBuffer.Clear();

				undoStep = 0;
				modified = false;
				parsed   = false;

				this.InvokeChange();
			}
		}

		/// <summary>
		/// Gets or sets the array of string that represents the lines in the SyntaxDocument.
		/// </summary>
		public string[] Lines
		{
			get { return this.Text.Split("\n".ToCharArray()); }
			set
			{
				string newText = "";
                Array.ForEach(value, lineText => newText += String.Format("{0}\n", lineText));

				this.Text = newText.Substring(0, newText.Length - 1);
			}
		}

		/// <summary>
		/// Gets or sets the SyntaxDocument's undo buffer's maximum size.
		/// </summary>
		public int MaxUndoBufferSize
		{
			get { return undoBuffer.MaxSize; }
			set { undoBuffer.MaxSize = value; }
		}

		/// <summary>
		/// Gets or sets the version of the SyntaxDocument.
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public long Version
		{
			get { return version; }
			set { version = value; }
		}

		/// <summary>
		/// Gets or sets the current undo step of the SyntaxDocument.
		/// </summary>
		public int UndoStep
		{
			get
			{
				if (undoStep > UndoBuffer.Count)
					undoStep = UndoBuffer.Count;

				return undoStep;
			}
			set { undoStep = value; }
		}

		/// <summary>
		/// Gets or sets whether the SyntaxDocument is folding.
		/// </summary>
		public bool Folding
		{
			get { return folding; }
			set
			{
				folding = value;
				if (value == false)
				{
					foreach (Row row in this)
						row.Expanded = true;
				}

				this.ResetVisibleRows();

				try
				{
					this.OnChanged();
				}
				catch { }
			}
		}

		/// <summary>
		/// Gets or sets the file that the SyntaxDocument gets language information from.
		/// </summary>
		public string LanguageFile
		{
			get { return languageFile; }
			set
			{
				languageFile = value;

				this.Parser.Init(value);
				this.Text = this.Text;	// Reset.
			}
		}

		/// <summary>
		/// Gets or sets whether the SyntaxDocument is modified.
		/// </summary>
		public bool Modified
		{
			get { return modified; }
			set
			{
				modified = value;
				this.OnModifiedChanged();
			}
		}

		/// <summary>
		/// Gets whether the SyntaxDocument is parsed.
		/// </summary>
		[Browsable(false)]
		public bool Parsed
		{
			get { return parsed; }
		}

		/// <summary>
		/// Returns the row at the given index in the SyntaxDocument.
		/// </summary>
		/// <param name="index">Index of the row.</param>
		/// <returns>The row at the given index in the SyntaxDocument.</returns>
		public Row this[int index]
		{
			get
			{
				if (index < 0 || index >= document.Count)
					return null;

				return document[index];
			}
			set { document[index] = value; }
		}

		#endregion

		#region Methods

		#region Public

		/// <summary>
		/// Raises the version of the SyntaxDocument.
		/// </summary>
		public void ChangeVersion()
		{
			this.Version++;
			if (this.Version > long.MaxValue - 10)
				this.Version = long.MinValue;
		}

		/// <summary>
		/// Starts capturing undoable actions.
		/// </summary>
		public void StartUndoCapture()
		{
			captureMode = true;
			captureBlock = new UndoBlockCollection();
		}

		/// <summary>
		/// Ends capturing undoable actions and returns the captured actions.
		/// </summary>
		/// <returns>The captured actions.</returns>
		public UndoBlockCollection EndUndoCapture()
		{
			captureMode = false;
			this.AddToUndoList(captureBlock);

			return captureBlock;
		}

		/// <summary>
		/// Reparses the SyntaxDocument.
		/// </summary>
		public void Reparse()
		{
			// Reset text property to tell the parser to reparse the SyntaxDocument.
			this.Text = this.Text;
		}

		/// <summary>
		/// Clears the bookmarks of the SyntaxDocument.
		/// </summary>
		public void ClearBookmarks()
		{
			foreach (Row row in this)
				row.Bookmarked = false;

			this.InvokeChange();
		}

		/// <summary>
		/// Clears the breakpoints of the SyntaxDocument.
		/// </summary>
		public void ClearBreakpoints()
		{
			foreach (Row row in this)
				row.Breakpoint = false;

			this.InvokeChange();
		}

		/// <summary>
		/// Ensures that the SyntaxDocument and the given row are parsed.
		/// </summary>
		/// <param name="row">Row to ensure is parsed.</param>
		public void EnsureParsed(Row row)
		{
			this.ParseAll();
			this.Parser.ParseLine(row.Index, true);
		}

		/// <summary>
		/// Invokes the Changed event.
		/// </summary>
		public void InvokeChange()
		{
			this.OnChanged();
		}

		/// <summary>
		/// Invokes the BreakpointAdded event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		public virtual void InvokeBreakpointAdded(Row row)
		{
			this.OnBreakpointAdded(row);
		}

		/// <summary>
		/// Invokes the BreakpointRemoved event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		public virtual void InvokeBreakpointRemoved(Row row)
		{
			this.OnBreakpointRemoved(row);
		}

		/// <summary>
		/// Invokes the BookmarkAdded event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		public virtual void InvokeBookmarkAdded(Row row)
		{
			this.OnBookmarkAdded(row);
		}

		/// <summary>
		/// Invokes the BookmarkRemoved event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		public virtual void InvokeBookmarkRemoved(Row row)
		{
			this.OnBookmarkRemoved(row);
		}

		/// <summary>
		/// Parses the SyntaxDocument.
		/// </summary>
		public void ParseAll()
		{
			while (this.ParseQueue.Count > 0)
				this.ParseSome();

			this.ParseQueue.Clear();
		}

		/// <summary>
		/// Parses the SyntaxDocument.
		/// </summary>
		/// <param name="parseKeywords">Whether to parse keywords.</param>
		public void ParseAll(bool parseKeywords)
		{
			this.ParseAll();
			if (parseKeywords == true)
			{
				for (int i = 0; i < Count; i++)
				{
					if (this[i].RowState != RowState.AllParsed)
						this.Parser.ParseLine(i, true);
				}

				this.ParseQueue.Clear();
				this.KeywordQueue.Clear();
			}
		}

		/// <summary>
		/// Collapses all collapsable rows in the SyntaxDocument.
		/// </summary>
		public void CollapseAll()
		{
			this.ParseAll(false);
			foreach (Row row in this)
				row.Expanded = false;

			this.ResetVisibleRows();
			this.OnChanged();
		}

		/// <summary>
		/// Expands all expandable rows in the SyntaxDocument.
		/// </summary>
		public void ExpandAll()
		{
			this.ParseAll(false);
			foreach (Row row in this)
				row.Expanded = true;

			this.ResetVisibleRows();
			this.OnChanged();
		}

		/// <summary>
		/// Parses the first 1000 rows of the SyntaxDocument.
		/// </summary>
		public void ParseSome()
		{
			this.ParseSome(1000);
		}

		/// <summary>
		/// Parses the given number of rows.
		/// </summary>
		/// <param name="rowCount">Number of rows to parse.</param>
		public void ParseSome(int rowCount)
		{
			if (ParseQueue.Count > 0)
			{
				parsed = false;
				int rowIndex = 0;

				while (rowIndex < rowCount && ParseQueue.Count > 0)
				{
					Row row = this.ParseQueue[0];
					rowIndex += this.ParseRows(row);
				}

				if (this.ShouldResetRows == true)
					this.ResetVisibleRows();

				if (this.Parsing != null)
					this.Parsing(this, new EventArgs());
			}
			else
			{
				if (parsed == false && modified == false)
				{
					parsed = true;

					foreach (Row row in this)
					{
						if (row.ExpansionStartSegment != null && row.ExpansionEndRow != null)
						{
							if (row.ExpansionStartSegment.Scope.DefaultExpanded == false)
								row.Expanded = false;
						}
					}

					this.ResetVisibleRows();
					if (this.ParsingCompleted != null)
						this.ParsingCompleted(this, new EventArgs());
				}
			}

			if (this.ParseQueue.Count == 0 && this.KeywordQueue.Count > 0)
			{
				int rowIndex = 0;
				while (rowIndex < rowCount / 20 && this.KeywordQueue.Count > 0)
				{
					Row row = this.KeywordQueue[0];
					rowIndex += this.ParseRows(row, true);
				}
			}
		}

		/// <summary>
		/// Adds the given text to the SyntaxDocument.
		/// </summary>
		/// <param name="text">Text to add.</param>
		/// <returns>The row created when adding the given text.</returns>
		public Row Add(string text)
		{
			Row row = new Row();

			row.Document = this;
			row.Text = text;

			document.Add(row);
			return row;
		}

		/// <summary>
		/// Inserts the given text at the given row index.
		/// </summary>
		/// <param name="text">Text to insert.</param>
		/// <param name="index">Index to insert the text at.</param>
		/// <returns>The row created when inserting the text.</returns>
		public Row Insert(string text, int index)
		{
			return Insert(text, index, true);
		}

		/// <summary>
		/// Inserts the given text at the given row index.
		/// </summary>
		/// <param name="text">Text to insert.</param>
		/// <param name="index">Index to insert text at.</param>
		/// <param name="storeUndoableAction">Whether to store the action as an UndoBlock.</param>
		/// <returns>The row created when inserting the text.</returns>
		public Row Insert(string text, int index, bool storeUndoableAction)
		{
			Row row = new Row();
			row.Document = this;

			document.Insert(index, row);
			row.Text = text;

			if (storeUndoableAction == true)
			{
				UndoBlock undoBlock = new UndoBlock();

				undoBlock.Text = text;
				undoBlock.Position.Y = this.IndexOf(row);

				AddToUndoList(undoBlock);
			}

			return row;
		}

		/// <summary>
		/// Removes the row at the given index.
		/// </summary>
		/// <param name="index">Index to remove row at.</param>
		public void Remove(int index)
		{
			this.Remove(index, true);
		}

		/// <summary>
		/// Removes the row at the given index.
		/// </summary>
		/// <param name="index">Index to remove row at.</param>
		/// <param name="storeUndoableAction">Whether to store the action as an UndoBlock.</param>
		public void Remove(int index, bool storeUndoableAction)
		{
			this.Remove(index, storeUndoableAction, true);
		}

		/// <summary>
		/// Removes the row at the given index.
		/// </summary>
		/// <param name="index">Index to remove row at.</param>
		/// <param name="storeUndoableAction">Whether to store the action as an UndoBlock.</param>
		/// <param name="raiseChangedEvent">Whether to raise the Changed event.</param>
		public void Remove(int index, bool storeUndoableAction, bool raiseChangedEvent)
		{
			Row row = this[index];

			if (storeUndoableAction == true)
			{
				TextRange textRange = new TextRange();

				if (index != this.Count - 1)
				{
					textRange.FirstColumn = 0;

					textRange.FirstRow = index;
					textRange.LastRow = index + 1;

					textRange.LastColumn = 0;
				}
				else
				{
					textRange.FirstColumn = row.PreviousRow.Text.Length;

					textRange.FirstRow = index - 1;
					textRange.LastRow = index;

					textRange.LastColumn = row.Text.Length;
				}

				this.PushUndoBlock(UndoAction.DeleteRange, GetRange(textRange), textRange.FirstColumn, textRange.FirstRow);
			}

			document.RemoveAt(index);
			if (row.Index > -1 && row.InKeywordQueue == true)
				this.KeywordQueue.Remove(row);

			if (row.Index > -1 && row.InQueue == true)
				this.ParseQueue.Remove(row);

			this.OnRowDeleted(row);
			if (raiseChangedEvent == true)
				this.OnChanged();
		}

		/// <summary>
		/// Deletes the given TextRange.
		/// </summary>
		/// <param name="textRange">TextRange to delete.</param>
		public void DeleteRange(TextRange textRange)
		{
			this.DeleteRange(textRange, true);
		}

		/// <summary>
		/// Parses the given row.
		/// </summary>
		/// <param name="row">Row to parse.</param>
		public void ParseRow(Row row)
		{
			this.ParseRow(row, false);
		}

		/// <summary>
		/// Parses the given row.
		/// </summary>
		/// <param name="row">Row to parse.</param>
		/// <param name="parseKeywords">Whether to parse keywords.</param>
		public void ParseRow(Row row, bool parseKeywords)
		{
			int index = this.IndexOf(row);
			if (index >= 0)
			{
				if (index > 0)
				{
					if (this[index - 1].InQueue)
						this.ParseRow(this[index - 1]);
				}

				this.Parser.ParseLine(index, false);
				if (parseKeywords == true)
					this.Parser.ParseLine(index, true);
			}

			int rowIndex = this.ParseQueue.IndexOf(row);

			if (rowIndex >= 0)
				this.ParseQueue.RemoveAt(rowIndex);

			row.InQueue = false;
		}

		/// <summary>
		/// Returns the row index of the next bookmark.
		/// </summary>
		/// <param name="startIndex">Index to start the search from.</param>
		/// <returns>Index of the found bookmark.</returns>
		public int GetNextBookmark(int startIndex)
		{
			for (int rowIndex = startIndex + 1; rowIndex < Count; rowIndex++)
			{
				Row row = this[rowIndex];
				if (row.Bookmarked == true)
					return rowIndex;
			}

			for (int rowIndex = 0; rowIndex < startIndex; rowIndex++)
			{
				Row row = this[rowIndex];
				if (row.Bookmarked == true)
					return rowIndex;
			}

			return startIndex;
		}

		/// <summary>
		/// Returns the row index of the previous bookmark.
		/// </summary>
		/// <param name="startIndex">Index to start the search from.</param>
		/// <returns>Index of the found bookmark.</returns>
		public int GetPreviousBookmark(int startIndex)
		{
			for (int rowIndex = startIndex - 1; rowIndex >= 0; rowIndex--)
			{
				Row row = this[rowIndex];
				if (row.Bookmarked == true)
					return rowIndex;
			}

			for (int rowIndex = this.Count - 1; rowIndex >= startIndex; rowIndex--)
			{
				Row row = this[rowIndex];
				if (row.Bookmarked == true)
					return rowIndex;
			}

			return startIndex;
		}

		/// <summary>
		/// Replaces the range.
		/// </summary>
		/// <param name="textRange">The text range.</param>
		/// <param name="replacedText">The replaced text.</param>
		/// <param name="storeUndoableAction">if set to <c>true</c> [store undoable action].</param>
		/// <returns>The replaced TextRange.</returns>
		public TextRange ReplaceRange(TextRange textRange, string replacedText, bool storeUndoableAction)
		{
			TextRange newTextRange = textRange;
			this.Modified = true;

			string deletedText = "";

			int rowIndex = 0;
			Row row      = null;
			
			if (storeUndoableAction == true)
				deletedText = this.GetRange(textRange);

			if (newTextRange.FirstRow == newTextRange.LastRow)
			{
				row = this[newTextRange.FirstRow];
				int maximumLength = Math.Min(newTextRange.FirstColumn, row.Text.Length);

				string startText = row.Text.Substring(0, maximumLength);
				string lastText  = "";

				if (row.Text.Length >= newTextRange.LastColumn)
					lastText = row.Text.Substring(newTextRange.LastColumn);

				row.Text = startText + lastText;
			}
			else
			{
				if (newTextRange.LastRow > Count - 1)
					newTextRange.LastRow = Count - 1;

				Row firstRow = this[newTextRange.FirstRow];
				if (newTextRange.FirstColumn > firstRow.Text.Length)
				{
					int    whitespaceLength = newTextRange.FirstColumn - firstRow.Text.Length;
					string whitespace       = new string(' ', whitespaceLength);

					this.InsertText(whitespace, firstRow.Text.Length, newTextRange.FirstRow, true);
				}

				string firstRowText = firstRow.Text.Substring(0, newTextRange.FirstColumn);
				Row lastRow = this[newTextRange.LastRow];

				int    maximumLength = Math.Min(lastRow.Text.Length, newTextRange.LastColumn);
				string lastRowText   = lastRow.Text.Substring(maximumLength);

				int startIndex = newTextRange.FirstRow;
				int endIndex   = newTextRange.LastRow;

				for (rowIndex = endIndex - 1; rowIndex >= startIndex; rowIndex--)
					this.Remove(rowIndex, false, false);

				Row newRow = this[startIndex];

				newRow.Expanded = true;
				newRow.Text = firstRowText + lastRowText;

				newRow.StartSegments.Clear();
				newRow.EndSegments.Clear();

				newRow.StartSegment = null;
				newRow.EndSegment = null;
				newRow.Parse();
			}

			if (this.FormatRanges != null)
				this.FormatRanges.Shrink(textRange);

			string text = replacedText;
			int x = newTextRange.FirstColumn;
			int y = newTextRange.FirstRow;

			this.Modified = true;
			row = this[y];

			if (x > row.Text.Length)
			{
				int paddingLength = x - row.Text.Length;
				string padding = new string(' ', paddingLength);

				text = padding + text;
				x -= paddingLength;
			}

			string startRowText = row.Text.Substring(0, x);
			string endRowText   = row.Text.Substring(x);

			string newRowText = startRowText + text + endRowText;
			string formattedRowText = newRowText.Replace(Environment.NewLine, "\n");
			string[] lines = formattedRowText.Split('\n');

			row.Text = lines[0];

			Row newLastRow = row;
			row.Parse();

			if (row.InQueue == false)
				ParseQueue.Add(row);

			row.InQueue = true;
			rowIndex = this.IndexOf(row);

			for (int lineIndex = 1; lineIndex < lines.Length; lineIndex++)
				newLastRow = this.Insert(lines[lineIndex], lineIndex + rowIndex, false);

			if (storeUndoableAction == true)
				this.PushUndoBlock(UndoAction.ReplaceRange, deletedText, text, newTextRange.FirstColumn, newTextRange.FirstRow);

			if (this.FormatRanges != null)
				this.FormatRanges.Expand(x, y, text);

			this.ResetVisibleRows();
			this.OnChanged();

			return new TextRange(newTextRange.FirstColumn, newTextRange.FirstRow, newLastRow.Text.Length - endRowText.Length, this.IndexOf(newLastRow));
		}

		/// <summary>
		/// Deletes the range.
		/// </summary>
		/// <param name="textRange">The text range.</param>
		/// <param name="storeUndoableAction">if set to <c>true</c> [store undoable action].</param>
		public void DeleteRange(TextRange textRange, bool storeUndoableAction)
		{
			TextRange newTextRange = textRange;
			this.Modified = true;

			if (storeUndoableAction == true)
			{
				string deletedText = this.GetRange(textRange);
				this.PushUndoBlock(UndoAction.DeleteRange, deletedText, newTextRange.FirstColumn, newTextRange.FirstRow);
			}

			if (newTextRange.FirstRow == newTextRange.LastRow)
			{
				Row firstRow = this[newTextRange.FirstRow];
				int maximumLength = Math.Min(newTextRange.FirstColumn, firstRow.Text.Length);

				string startText = firstRow.Text.Substring(0, maximumLength);
				string lastText = "";

				if (firstRow.Text.Length >= newTextRange.LastColumn)
					lastText = firstRow.Text.Substring(newTextRange.LastColumn);

				firstRow.Text = startText + lastText;
			}
			else
			{
				if (newTextRange.LastRow > Count - 1)
					newTextRange.LastRow = Count - 1;

				Row firstRow = this[newTextRange.FirstRow];
				if (newTextRange.FirstColumn > firstRow.Text.Length)
				{
					int whitespaceLength = newTextRange.FirstColumn - firstRow.Text.Length;
					string whitespace = new string(' ', whitespaceLength);

					this.InsertText(whitespace, firstRow.Text.Length, newTextRange.FirstRow, true);
				}

				string firstRowText = firstRow.Text.Substring(0, newTextRange.FirstColumn);
				Row lastRow = this[newTextRange.LastRow];

				int maximumLength = Math.Min(lastRow.Text.Length, newTextRange.LastColumn);
				string lastRowText = lastRow.Text.Substring(maximumLength);

				int startIndex = newTextRange.FirstRow;
				int endIndex = newTextRange.LastRow;

				for (int rowIndex = endIndex - 1; rowIndex >= startIndex; rowIndex--)
					this.Remove(rowIndex, false, false);

				Row newRow = this[startIndex];

				newRow.Expanded = true;
				newRow.Text = firstRowText + lastRowText;

				newRow.StartSegments.Clear();
				newRow.EndSegments.Clear();

				newRow.StartSegment = null;
				newRow.EndSegment = null;
				newRow.Parse();
			}

			if (this.FormatRanges != null)
				this.FormatRanges.Shrink(textRange);

			this.ResetVisibleRows();
			this.OnChanged();
		}

		/// <summary>
		/// Gets the range's text.
		/// </summary>
		/// <param name="textRange">The text range.</param>
		/// <returns>The range's text.</returns>
		public string GetRange(TextRange textRange)
		{
			if (textRange.FirstRow >= Count)
				textRange.FirstRow = Count;

			if (textRange.LastRow >= Count)
				textRange.LastRow = Count;

			if (textRange.FirstRow != textRange.LastRow)
			{
				Row firstRow = this[textRange.FirstRow];
				int maximumLength = Math.Min(firstRow.Text.Length, textRange.FirstColumn);
				string rowText = firstRow.Text.Substring(maximumLength) + Environment.NewLine;

				Row lastRow = this[textRange.LastRow];
				if (lastRow == null)
					return "";

				int lastMaximumLength = Math.Min(lastRow.Text.Length, textRange.LastColumn);
				string lastRowText = lastRow.Text.Substring(0, lastMaximumLength);

				StringBuilder stringBuilder = new StringBuilder();
				for (int rowIndex = textRange.FirstRow + 1; rowIndex <= textRange.LastRow - 1; rowIndex++)
				{
					Row currentRow = this[rowIndex];
					stringBuilder.Append(currentRow.Text + Environment.NewLine);
				}

				return rowText + stringBuilder.ToString() + lastRowText;
			}
			else
			{
				Row row = this[textRange.FirstRow];

				int maximumLength = Math.Min(row.Text.Length, textRange.LastColumn);

				int actualLength = maximumLength - textRange.FirstColumn;
				if (actualLength <= 0)
					return "";

				return row.Text.Substring(textRange.FirstColumn, maximumLength - textRange.FirstColumn);
			}
		}

		/// <summary>
		/// Returns the index of the given row.
		/// </summary>
		/// <param name="row">The row to find index of.</param>
		/// <returns>The found index.</returns>
		public int IndexOf(Row row)
		{
			return document.IndexOf(row);
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
		public IEnumerator GetEnumerator()
		{
			return document.GetEnumerator();
		}

		/// <summary>
		/// Clears the SyntaxDocument.
		/// </summary>
		public void ClearAll()
		{
			foreach (Row row in document)
				this.OnRowDeleted(row);

			document.Clear();
			this.ParseQueue.Clear();
			this.KeywordQueue.Clear();

			undoBuffer.Clear();
			undoStep = 0;
		}

		/// <summary>
		/// Clears this instance.
		/// </summary>
		public void Clear()
		{
			this.Text = "";
		}

		/// <summary>
		/// Inserts the text.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="x">The x coordinate to insert at.</param>
		/// <param name="y">The y coordinate to insert at.</param>
		/// <returns>The TextPoint where the text was added.</returns>
		public TextPoint InsertText(string text, int x, int y)
		{
			return this.InsertText(text, x, y, true);
		}

		/// <summary>
		/// Inserts the text.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="x">The x coordinate to insert at.</param>
		/// <param name="y">The y coordinate to insert at.</param>
		/// <param name="storeUndoableAction">if set to <c>true</c> [store undoable action].</param>
		/// <returns>The TextPoint where the text was added.</returns>
		public TextPoint InsertText(string text, int x, int y, bool storeUndoableAction)
		{
			this.Modified = true;
			Row row = this[y];

			if (x > row.Text.Length)
			{
				int paddingLength = x - row.Text.Length;
				string padding = new string(' ', paddingLength);

				text = padding + text;
				x -= paddingLength;
			}

			string startRowText = row.Text.Substring(0, x);
			string endRowText   = row.Text.Substring(x);

			string newText = startRowText + text + endRowText;
			string formattedRowText = newText.Replace(Environment.NewLine, "\n");
			string[] lines = formattedRowText.Split('\n');

			row.Text = lines[0];
			Row newLastRow = row;
			row.Parse();

			if (row.InQueue == false)
				this.ParseQueue.Add(row);

			row.InQueue = true;

			int rowIndex = this.IndexOf(row);
			for (int lineIndex = 1; lineIndex < lines.Length; lineIndex++)
				newLastRow = this.Insert(lines[lineIndex], lineIndex + rowIndex, false);

			if (storeUndoableAction == true)
				this.PushUndoBlock(UndoAction.InsertRange, text, x, y);

			if (this.FormatRanges != null)
				this.FormatRanges.Expand(x, y, text);

			this.ResetVisibleRows();
			this.OnChanged();

			return new TextPoint(newLastRow.Text.Length - endRowText.Length, this.IndexOf(newLastRow));
		}

		/// <summary>
		/// Pushes the undo block to the stack.
		/// </summary>
		/// <param name="undoAction">The undo action.</param>
		/// <param name="text">The text.</param>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		public void PushUndoBlock(UndoAction undoAction, string text, int x, int y)
		{
			this.PushUndoBlock(undoAction, text, "", x, y);
		}

		/// <summary>
		/// Pushes the undo block to the stack.
		/// </summary>
		/// <param name="undoAction">The undo action.</param>
		/// <param name="text">The text.</param>
		/// <param name="replacedText">The replaced text.</param>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		public void PushUndoBlock(UndoAction undoAction, string text, string replacedText, int x, int y)
		{
			UndoBlock undoBlock = new UndoBlock();
			undoBlock.Action = undoAction;
			undoBlock.Text = text;

			undoBlock.ReplacedText = replacedText;
			undoBlock.Position.Y = y;
			undoBlock.Position.X = x;

			if (captureMode == true)
				captureBlock.Add(undoBlock);
			else
				this.AddToUndoList(undoBlock);
		}

		/// <summary>
		/// Returns the range from the text at the given coordinates.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="x">The x coordinate.</param>
		/// <param name="y">The y coordinate.</param>
		/// <returns>The found range.</returns>
		public TextRange GetRangeFromText(string text, int x, int y)
		{
			string formattedText = text.Replace(Environment.NewLine, "\n");
			string[] lines = formattedText.Split("\n".ToCharArray());

			TextRange textRange = new TextRange();
			textRange.FirstColumn = x;
			textRange.FirstRow = y;

			textRange.LastRow = lines.Length - 1 + y;
			textRange.LastColumn = lines[lines.Length - 1].Length;

			if (textRange.FirstRow == textRange.LastRow)
				textRange.LastColumn += textRange.FirstColumn;

			return textRange;
		}

		/// <summary>
		/// Adds the undo block to the undo list.
		/// </summary>
		/// <param name="undoBlock">The undo block.</param>
		public void AddToUndoList(UndoBlock undoBlock)
		{
			UndoBlockCollection undoBlockCollection = new UndoBlockCollection();
			undoBlockCollection.Add(undoBlock);

			this.AddToUndoList(undoBlockCollection);
		}

		/// <summary>
		/// Adds the undo block collection to the undo list.
		/// </summary>
		/// <param name="undoBlockCollection">The undo block collection.</param>
		public void AddToUndoList(UndoBlockCollection undoBlockCollection)
		{
			undoBuffer.ClearFrom(undoStep);
			undoBuffer.Add(undoBlockCollection);

			undoStep++;
			this.OnUndoBufferChanged();
		}

		/// <summary>
		/// Indents the given segment.
		/// </summary>
		/// <param name="segment">The segment.</param>
		public void AutoIndentSegment(Segment segment)
		{
			if (segment == null)
				segment = this[0].StartSegment;

			Row startRow = segment.StartRow;
			Row endRow   = segment.EndRow;

			if (startRow == null)
				startRow = this[0];

			if (endRow == null)
				endRow = this[Count - 1];

			for (int rowIndex = startRow.Index; rowIndex <= endRow.Index; rowIndex++)
			{
				Row row = this[rowIndex];
				int indentationDepth = row.Indent;

				string text = row.Text.Substring(row.GetLeadingWhitespace().Length);
				string indent = new string(' ', indentationDepth);

				row.Text = indent + text;
			}

			this.ResetVisibleRows();
		}

		/// <summary>
		/// Returns the segment from the given text point.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found segment.</returns>
		public Segment GetSegmentFromPos(TextPoint textPoint)
		{
			Row row = this[textPoint.Y];
			int charIndex = 0;

			if (row == null)
				return null;

			if (row.Count == 0)
				return row.StartSegment;

			Segment previousSegment = row.StartSegment;
			Word    currentWord     = null;

			for (int i = 0; i < row.Count; i++)
			{
				currentWord = row[i];

				if (currentWord.Text.Length + charIndex > textPoint.X)
				{
					if (charIndex == textPoint.X)
						return previousSegment;
					else
						return currentWord.Segment;
				}
				else
				{
					charIndex += currentWord.Text.Length;
					previousSegment = currentWord.Segment;
				}
			}

			return row.EndSegment;
		}

		/// <summary>
		/// Returns the word from the given text point.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found word.</returns>
		public Word GetWordFromPos(TextPoint textPoint)
		{
			Row row = this[textPoint.Y];
			if (row == null)
				return null;

			int charIndex = 0;

			Word correctWord = null;
			Word currentWord = null;

			for (int i = 0; i < row.Count; i++)
			{
				currentWord = row[i];

				if (correctWord != null)
				{
					if (currentWord.Text == "")
						return currentWord;
					else
						return correctWord;
				}

				if (currentWord.Text.Length + charIndex > textPoint.X ||currentWord == row[row.Count - 1])
					correctWord = currentWord;
				else
					charIndex += currentWord.Text.Length;
			}

			return correctWord;
		}

		/// <summary>
		/// Returns the formatted word from the given text point.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found word.</returns>
		public Word GetFormatWordFromPos(TextPoint textPoint)
		{
			Row row = this[textPoint.Y];
			int charIndex = 0;

			Word correctWord = null;
			Word currentWord = null;

			for (int i = 0; i < row.FormattedWords.Count; i++)
			{
				currentWord = row.FormattedWords[i];

				if (correctWord != null)
				{
					if (currentWord.Text == "")
						return currentWord;
					else
						return correctWord;
				}

				if (currentWord.Text.Length + charIndex > textPoint.X || currentWord == row[row.Count - 1])
					correctWord = currentWord;
				else
					charIndex += currentWord.Text.Length;
			}

			return correctWord;
		}

		/// <summary>
		/// Invokes the RowParsed event.
		/// </summary>
		/// <param name="row">The row.</param>
		public void InvokeRowParsed(Row row)
		{
			this.OnRowParsed(row);
		}

		/// <summary>
		/// Resets the visible rows.
		/// </summary>
		public void ResetVisibleRows()
		{
			this.InternalResetVisibleRows();
		}

		/// <summary>
		/// Returns the y coordinate of the given text point, recalculating it first.
		/// </summary>
		/// <param name="textPoint">The text point.</param>
		/// <returns>The found y coordinate.</returns>
		public int PointToIntPos(TextPoint textPoint)
		{
			int y = 0;
			int position = 0;
			foreach (Row row in this)
			{
				if (y == textPoint.Y)
					break;

				position += row.Text.Length + Environment.NewLine.Length;
				y++;
			}

			return position + Math.Min(textPoint.X, this[textPoint.Y].Text.Length);
		}

		/// <summary>
		/// Returns the text point of the given point as an x coordinate.
		/// </summary>
		/// <param name="point">The text point.</param>
		/// <returns>The found text point.</returns>
		public TextPoint IntPosToPoint(int point)
		{
			int position = 0;
			int y = 0;
			int x = 0;

			foreach (Row row in this)
			{
				position += row.Text.Length + Environment.NewLine.Length;
				if (position > point)
				{
					position -= row.Text.Length + Environment.NewLine.Length;
					x = point - position;

					return new TextPoint(x, y);
				}

				y++;
			}

			return new TextPoint(-1, -1);
		}

		/// <summary>
		/// Toggles the row expansion.
		/// </summary>
		/// <param name="row">The row.</param>
		public void ToggleRowExpansion(Row row)
		{
			if (folding == false)
				return;

			if (row.ExpansionEndRow == null || row.ExpansionStartRow == null)
				return;

			if (row.CanFold == true)
				row.Expanded = row.Expanded == false;

			this.ResetVisibleRows();
			this.OnChanged();
		}

		/// <summary>
		/// Gets the start bracket word.
		/// </summary>
		/// <param name="startWord">The start word.</param>
		/// <param name="endPattern">The end pattern.</param>
		/// <param name="findSegment">The find segment.</param>
		/// <returns>The found word.</returns>
		public Word GetStartBracketWord(Word startWord, Pattern endPattern, Segment findSegment)
		{
			if (startWord == null || startWord.Pattern == null || startWord.Segment == null)
				return null;

			int currentRow = startWord.Row.Index;
			int firstRow  = findSegment.StartRow.Index;

			int x = startWord.Index;
			int count = 0;

			while (currentRow >= firstRow)
			{
				for (int rowIndex = x; rowIndex >= 0; rowIndex--)
				{
					Word currentWord = this[currentRow][rowIndex];
					if (currentWord.Segment == findSegment && currentWord.Type == WordType.Word)
					{
						if (currentWord.Pattern == startWord.Pattern)
							count++;

						if (currentWord.Pattern == endPattern)
							count--;

						if (count == 0)
							return currentWord;
					}
				}

				if (startWord.Pattern.IsMultilineBracket == false)
					break;

				currentRow--;
				if (currentRow >= 0)
					x = this[currentRow].Count - 1;
			}

			return null;
		}

		/// <summary>
		/// Gets the end bracket word.
		/// </summary>
		/// <param name="startWord">The start word.</param>
		/// <param name="endPattern">The end pattern.</param>
		/// <param name="findSegment">The find segment.</param>
		/// <returns>The found word.</returns>
		public Word GetEndBracketWord(Word startWord, Pattern endPattern, Segment findSegment)
		{
			if (startWord == null || startWord.Pattern == null || startWord.Segment == null)
				return null;

			int currentRow = startWord.Row.Index;
			int lastRow = Count - 1;

			if (findSegment.EndRow != null)
				lastRow = findSegment.EndRow.Index;

			int x = startWord.Index;
			int count = 0;
			while (currentRow <= lastRow)
			{
				for (int rowIndex = x; rowIndex < this[currentRow].Count; rowIndex++)
				{
					Word currentWord = this[currentRow][rowIndex];
					if (currentWord.Segment == findSegment && currentWord.Type == WordType.Word)
					{
						if (currentWord.Pattern == startWord.Pattern)
							count++;

						if (currentWord.Pattern == endPattern)
							count--;

						if (count == 0)
							return currentWord;
					}
				}

				if (startWord.Pattern.IsMultilineBracket == false)
					break;

				currentRow++;
				x = 0;
			}

			return null;
		}

		/// <summary>
		/// Undoes the last undoable action.
		/// </summary>
		/// <returns>The position of the undone action.</returns>
		public TextPoint Undo()
		{
			if (undoStep == 0)
				return new TextPoint(-1, -1);

			UndoBlockCollection undoBlockCollection = (UndoBlockCollection)UndoBuffer[UndoStep - 1];
			UndoBlock undoBlock = (UndoBlock)undoBlockCollection[0];

			TextRange textRange = null;
			for (int undoBlockIndex = undoBlockCollection.Count - 1; undoBlockIndex >= 0; undoBlockIndex--)
			{
				undoBlock = (UndoBlock)undoBlockCollection[undoBlockIndex];
				switch (undoBlock.Action)
				{
					case UndoAction.DeleteRange:
						this.InsertText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y, false);

						break;
					case UndoAction.InsertRange:
						textRange = this.GetRangeFromText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y);
						this.DeleteRange(textRange, false);

						break;
					case UndoAction.ReplaceRange:
						textRange = this.GetRangeFromText(undoBlock.ReplacedText, undoBlock.Position.X, undoBlock.Position.Y);

						this.DeleteRange(textRange, false);
						this.InsertText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y, false);

						break;
					default:
						break;
				}
			}

			undoStep--;
			this.ResetVisibleRows();

			if (undoStep == 0)
				Modified = false;

			TextPoint textPoint = new TextPoint(undoBlock.Position.X, undoBlock.Position.Y);
			this.OnUndoBufferChanged();

			return textPoint;
		}

		/// <summary>
		/// Redoes the last redoable action.
		/// </summary>
		/// <returns>The position of the redone action.</returns>
		public TextPoint Redo()
		{
			if (undoStep >= undoBuffer.Count)
				return new TextPoint(-1, -1);

			UndoBlockCollection undoBlockCollection = (UndoBlockCollection)undoBuffer[UndoStep];
			UndoBlock undoBlock = (UndoBlock)undoBlockCollection[0];

			TextRange textRange      = null;
			TextRange foundTextRange = null;

			for (int i = 0; i < undoBlockCollection.Count; i++)
			{
				undoBlock = (UndoBlock)undoBlockCollection[i];

				switch (undoBlock.Action)
				{
					case UndoAction.InsertRange:
						this.InsertText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y, false);
						foundTextRange = this.GetRangeFromText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y);

						break;
					case UndoAction.DeleteRange:
						textRange = this.GetRangeFromText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y);

						this.DeleteRange(textRange, false);
						foundTextRange = textRange;

						break;
					case UndoAction.ReplaceRange:
						textRange = this.GetRangeFromText(undoBlock.Text, undoBlock.Position.X, undoBlock.Position.Y);

						this.DeleteRange(textRange, false);
						this.InsertText(undoBlock.ReplacedText, undoBlock.Position.X, undoBlock.Position.Y, false);

						foundTextRange = this.GetRangeFromText(undoBlock.ReplacedText, undoBlock.Position.X, undoBlock.Position.Y);

						break;
					default:
						break;
				}
			}

			undoStep++;
			this.ResetVisibleRows();

			this.OnUndoBufferChanged();
			return new TextPoint(foundTextRange.LastColumn, foundTextRange.LastRow);
		}

		#endregion

		#region Protected

		/// <summary>
		/// Raises the BreakpointAdded event.
		/// </summary>
		/// <param name="row">Row to be associated with the event.</param>
		protected virtual void OnBreakpointAdded(Row row)
		{
			if (this.BreakpointAdded != null)
				this.BreakpointAdded(this, new RowEventArgs(row));
		}

		/// <summary>
		/// Raises the BreakpointRemoved event.
		/// </summary>
		/// <param name="row">Row to be associated with the event.</param>
		protected virtual void OnBreakpointRemoved(Row row)
		{
			if (this.BreakpointRemoved != null)
				this.BreakpointRemoved(this, new RowEventArgs(row));
		}

		/// <summary>
		/// Raises the BookmarkAdded event.
		/// </summary>
		/// <param name="row">Row to be associated with the event.</param>
		protected virtual void OnBookmarkAdded(Row row)
		{
			if (this.BookmarkAdded != null)
				this.BookmarkAdded(this, new RowEventArgs(row));
		}

		/// <summary>
		/// Raises the BookmarkRemoved event.
		/// </summary>
		/// <param name="row">Row to be associated with the event.</param>
		protected virtual void OnBookmarkRemoved(Row row)
		{
			if (this.BookmarkRemoved != null)
				this.BookmarkRemoved(this, new RowEventArgs(row));
		}

		/// <summary>
		/// Raises the UndoBufferChanged event.
		/// </summary>
		protected virtual void OnUndoBufferChanged()
		{
			if (this.UndoBufferChanged != null)
				this.UndoBufferChanged(this, EventArgs.Empty);
		}

		/// <summary>
		/// Applies the format ranges of the given row to each word in the given row.
		/// </summary>
		/// <param name="row">Row to apply format ranges to.</param>
		protected internal void OnApplyFormatRanges(Row row)
		{
			if (this.FormatRanges == null)
				return;

			if (this.FormatRanges.RowContainsFormats(row) == false || row.RowState != RowState.AllParsed)
				row.FormattedWords = row.Words;
			else
			{
				row.FormattedWords = new WordCollection();

				int x = 0;
				int textLength   = 0;
				int currentIndex = 0;
				int rowIndex     = row.Index;

				foreach (char character in row.Text)
				{
					Word        currentWord = row[currentIndex];
					FormatRange formatRange = this.FormatRanges.MergeFormatRanges(x, rowIndex);

					Word characterWord = new Word();
					characterWord.Text = character.ToString();

					if (formatRange == null)
						characterWord.Style = currentWord.Style;
					else
					{
						characterWord.Style = new TextStyle();
						if (currentWord.Style == null)
							currentWord.Style = new TextStyle();

						characterWord.Style.BackColor = formatRange.BackColor == Color.Empty ? currentWord.Style.BackColor : formatRange.BackColor;
						characterWord.Style.ForeColor = formatRange.ForeColor == Color.Empty ? currentWord.Style.ForeColor : formatRange.ForeColor;

						if (formatRange.WaveColor != Color.Empty)
						{
							characterWord.HasError = true;
							characterWord.ErrorColor = formatRange.WaveColor;
						}
					}

					characterWord.Type = currentWord.Type;
					characterWord.Segment = currentWord.Segment;
					row.FormattedWords.Add(characterWord);

					textLength++;
					if (textLength == row[currentIndex].Text.Length)
					{
						currentIndex++;
						textLength = 0;
					}

					x++;
				}
			}
		}

		#endregion

		#region Private

		/// <summary>
		/// Raises the ModifiedChanged event.
		/// </summary>
		private void OnModifiedChanged()
		{
			if (this.ModifiedChanged != null)
				this.ModifiedChanged(this, new EventArgs());
		}

		/// <summary>
		/// Raises the Changed event.
		/// </summary>
		private void OnChanged()
		{
			if (this.Changed != null)
				this.Changed(this, new EventArgs());
		}

		/// <summary>
		/// Raises the RowParsed event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		private void OnRowParsed(Row row)
		{
			if (this.RowParsed != null)
				this.RowParsed(this, new RowEventArgs(row));

			this.OnApplyFormatRanges(row);
		}

		/// <summary>
		/// Raises the RowDeleted event.
		/// </summary>
		/// <param name="row">Row to associate with the event.</param>
		private void OnRowDeleted(Row row)
		{
			if (this.RowDeleted != null)
				this.RowDeleted(this, new RowEventArgs(row));
		}

		/// <summary>
		/// Resets the visible rows of the SyntaxDocument.
		/// </summary>
		private void InternalResetVisibleRows()
		{
			if (folding == false)
			{
				this.VisibleRows = document;
				this.ShouldResetRows = false;
			}
			else
			{
				this.ShouldResetRows = false;
				this.VisibleRows = new RowCollection();

				int rowIndex = 0;
				Row row = null;

				for (int i = 0; i < Count; i++)
				{
					row = this[rowIndex];
					this.VisibleRows.Add(row);

					bool collapsed = false;

					if (row.CanFold == true)
						if (row.ExpansionStartSegment.Expanded == false)
						{
							if (row.ExpansionStartSegment.EndWord != null)
							{
								row = row.ExpansionEndRow;
								collapsed = true;
							}
						}

					if (collapsed == false)
						rowIndex++;
					else
						rowIndex = IndexOf(row) + 1;

					if (rowIndex >= Count)
						break;
				}
			}
		}

		/// <summary>
		/// Collapses the given row.
		/// </summary>
		/// <param name="row">Row to collapse.</param>
		private void CollapseRow(Row row)
		{
			Row startExpansionRow = row.ExpansionStartRow;
			Row endExpansionRow   = row.ExpansionEndRow;

			int count = endExpansionRow.VisibleIndex - startExpansionRow.VisibleIndex - 1;
			int startIndex = startExpansionRow.VisibleIndex + 1;

			for (int i = 0; i <= count; i++)
				this.VisibleRows.RemoveAt(startIndex);
		}

		/// <summary>
		/// Expands the given row.
		/// </summary>
		/// <param name="row">Row to expand.</param>
		private void ExpandRow(Row row)
		{
			Row startExpansionRow = row.ExpansionStartRow;
			Row endExpansionRow   = row.ExpansionEndRow;

			int count = endExpansionRow.Index - startExpansionRow.Index - 1;
			int startIndex = startExpansionRow.Index + 1;

			int visibleIndex = startExpansionRow.VisibleIndex + 1;
			for (int i = 0; i <= count; i++)
			{
				Row currentRow = this[startIndex + i];
				this.VisibleRows.Insert(visibleIndex, currentRow);

				if (currentRow.ExpansionStartSegment != null)
				{
					if (currentRow.ExpansionStartSegment.Expanded == false)
					{
						currentRow = currentRow.ExpansionStartSegment.EndRow;
						i = currentRow.Index - startIndex;
					}
				}

				visibleIndex++;
			}
		}

		/// <summary>
		/// Parses all rows that are in the parsing queue from the given row.
		/// </summary>
		/// <param name="row">Row to start parsing at.</param>
		/// <returns>Number of parsed rows.</returns>
		private int ParseRows(Row row)
		{
			return this.ParseRows(row, false);
		}

		/// <summary>
		/// Parses all rows that are in the parsing queue from the given row.
		/// </summary>
		/// <param name="row">Row to start parsing at.</param>
		/// <param name="parseKeywords">Whether to parse keywords.</param>
		/// <returns>Number of parsed rows.</returns>
		private int ParseRows(Row row, bool parseKeywords)
		{
			if (parseKeywords == false)
			{
				int rowIndex = this.IndexOf(row);
				int rowCount = 0;

				while (row.InQueue && rowCount < 100)
				{
					if (rowIndex >= 0)
					{
						if (rowIndex > 0)
						{
							if (this[rowIndex - 1].InQueue)
								this.ParseRow(this[rowIndex - 1]);
						}

						this.Parser.ParseLine(rowIndex, false);
					}

					int currentRowIndex = this.ParseQueue.IndexOf(row);
					if (currentRowIndex >= 0)
						this.ParseQueue.RemoveAt(currentRowIndex);

					row.InQueue = false;
					rowIndex++;
					rowCount++;

					row = this[rowIndex];

					if (row == null)
						break;
				}

				return rowCount;
			}
			else
			{
				int rowIndex = this.IndexOf(row);
				if (rowIndex == -1 || row.InKeywordQueue == false && this.KeywordQueue.Contains(row) == true)
				{
					this.KeywordQueue.Remove(row);
					return 0;
				}

				int rowCount = 0;
				while (row.InKeywordQueue && rowCount < 100)
				{
					if (rowIndex >= 0)
					{
						if (rowIndex > 0)
						{
							if (this[rowIndex - 1].InQueue)
								this.ParseRow(this[rowIndex - 1]);
						}

						this.Parser.ParseLine(rowIndex, true);
					}

					rowIndex++;
					rowCount++;

					row = this[rowIndex];

					if (row == null)
						break;
				}

				return rowCount;
			}
		}

		#endregion

		#region Component Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of SyntaxDocument.
		/// </summary>
		public SyntaxDocument()
		{
			this.Parser.Document = this;
			this.Text = "";

			this.VisibleRows = new RowCollection();
			this.ResetVisibleRows();

			Language language = new Language();

			language.MainBlock = new Block(language);
			language.MainBlock.Multiline = true;

			this.Parser.Init(language);
			this.FormatRanges = new FormatRangeCollection(this);
		}

		/// <summary>
		/// Initializes a new instance of SyntaxDocument.
		/// </summary>
		/// <param name="container">Container for the SyntaxDocument.</param>
		public SyntaxDocument(IContainer container)
			: this()
		{
			container.Add(this);
			InitializeComponent();
		}
	}
}
